--[[
@classmod CommandBar
]] 
local lume = require(LIB_PATH .. "lume")
local timer = require "lib.timer"
local Command = require "command"

return pl.class {

				resize_cursor = function(self, w, h)
								local font = self.font
								self.font_w = w or font:getWidth(self:get_overlapped_char())
								self.cursor_w = self.font_w
								self.font_h = h or font:getHeight(self:get_overlapped_char())
								self.cursor_h = self.font_h
				end,

				activate = function(self, bool)
								if bool then
												Sled.input:switch_mode("CommandMode")
												self.active = true
								else
												self.active = false
												Sled.input:switch_mode("EditMode")
								end
				end,

				cursor_blink = function(self) self.blink = not self.blink end,

				load_commands = function(self)
								local file_list = lume.map(pl.dir.getfiles("cmds", "*.lua"), function(v)
												local dir, file = pl.path.splitpath(v)
												local filename, ext = pl.path.splitext(file)
												return {filename, require("cmds." .. filename)}
								end)
								return file_list
				end,

				_init = function(self, font)
								self.font = font
								self.history = require "history"()
								self.blink_timer = timer.every(.4, function() self:cursor_blink() end)
								self.output_timer = timer.every(4, function() self.output = nil end)
								self.command_string = ""
								self.text_offset = 8
								self.x, self.y = 0, 0
								self.w = _lg.getWidth()
								self.y = _lg.getHeight() - 32
								self.h = 24
								self:move_cursor_to_eol()
								self:resize_cursor(self.font:getWidth(" "))
								self.cmd_list = self:load_commands()
								self:activate(false)
				end,

				go_up = function(self)
								self.command_string = self.history:go_up()
								self:move_cursor_to_eol()
				end,

				go_down = function(self)
								self.command_string = self.history:go_down()
								self:move_cursor_to_eol()
				end,

				add_char = function(self, c)
								if c then
												local cmd = self.command_string
												if not self:cursor_is_at_eol() then
																local j = self.cursor_position
																local pre_str = string.sub(cmd, 1, j)
																local post_str = string.sub(cmd, j - string.len(cmd))
																self.command_string = pre_str .. c .. post_str
												else
																self.command_string = self.command_string .. c or ""
												end
								end
								self:move_cursor(1)
				end,

				del_char = function(self)
								local j = self.cursor_position
								local cmd = self.command_string

								if not self:cursor_is_at_eol() then
												local pre_str = string.sub(cmd, 1, j)
												local post_str = string.sub(cmd, j - string.len(cmd))
												local l = string.len(pre_str)
												if l > 0 then
																self:move_cursor(-1)
																pre_str = string.sub(pre_str, 1, l - 1)
																self.command_string = pre_str .. post_str
												end
								else
												self.command_string = string.sub(cmd, 1, -2)
												self:move_cursor(-1)
								end
				end,

				update = function(self, dt)
								timer.update(dt)
								if not self.active then return end
								self.w = _lg.getWidth()
								self.y = _lg.getHeight() - self.h
								if not self:cursor_is_at_eol() then self:resize_cursor() end
				end,

				reset = function(self)
								self.command_string = ""
								self:move_cursor_to_eol()
				end,

				output_list = function(self, list)
								return lume.reduce(lume.map(list, function(v) return v[1] end),
								                   function(v, w)
												return string.format("%s %s", v, w)
								end)
				end,

				suggest = function(self)
								local words = pl.seq.copy(pl.input.words(self.command_string))
								-- if #words < 1 then return end
								local cmd = words[1]
								local target = words[2]
								local args = lume.slice(words, 3)
								if self:cmd_exists(cmd) then
												self:print(self:cmd_suggest(cmd, target, args))
								end
								if not cmd then self:print(self:output_list(self.cmd_list)) end
				end,

				cmd_exists = function(self, cmd)
								return lume.any(self.cmd_list, function(v) return v[1] == cmd end)
				end,

				cmd_get = function(self, cmd)
								local command
								for k, v in ipairs(self.cmd_list) do
												if v[1] == cmd then command = v[2] end
								end
								return command
				end,

				cmd_suggest = function(self, lcmd, target, args)
								local cmd = self:cmd_get(lcmd)
								return cmd:suggest(target, args)
				end,

				cmd_run = function(self, lcmd, target, args)
								local cmd = self:cmd_get(lcmd)
								assert(cmd:is_a(Command), cmd._name .. "isn't a command")
								return cmd:execute(target, args)
				end,

				eval = function(self)
								local words = pl.seq.copy(pl.input.words(self.command_string))
								if #words < 1 then return end
								local cmd = words[1]
								local target = words[2]
								local args = lume.slice(words, 3)
								if self:cmd_exists(cmd) then
												self:print(self:cmd_run(cmd, target, args))
								else
												self:print("unrecognized command.")
												return
								end
								self.history:add_entry(self.command_string)
								self:reset()
				end,

				get_overlapped_char = function(self)
								local cp = self.cursor_position + 1
								return string.sub(self.command_string, cp, cp)
				end,

				print = function(self, txt)
								self.output_timer.elapsed = 0
								self.output = txt
				end,

				draw_output = function(self)
								_lg.setLineWidth(.1)
								_lg.setColor(1, 1, 1, .2)
								_lg.rectangle("line", self.x, self.y - 25, self.w, self.h)
								if self.output then
												_lg.setColor(1, 1, 1, 1)
												_lg.print(self.output, self.x + 8, self.y - 25)
								end
				end,

				draw_bar_outline = function(self)
								_lg.setLineWidth(.1)
								_lg.setColor(1, 1, 1, 1)
								_lg.rectangle("line", self.x, self.y, self.w, self.h)
				end,

				draw_text = function(self)
								_lg.setColor(1, 1, 1, 1)
								_lg.print(self.command_string, self.x + self.text_offset, self.y)
				end,

				move_cursor = function(self, n)
								self.cursor_position = lume.clamp(self.cursor_position + n, 0,
								                                  string.len(self.command_string))
								local cp = self.cursor_position + 1
								if cp == string.len(self.command_string) + 1 or
												string.sub(self.command_string, cp, cp) == " " then
												self.is_on_char = false
								else
												self.is_on_char = true
								end
				end,

				move_cursor_to_eol = function(self)
								self.cursor_position = string.len(self.command_string or "")
								self.is_on_char = false
				end,

				cursor_is_at_eol = function(self)
								return self.cursor_position == string.len(self.command_string)
				end,

				cursor_is_at_bol = function(self) return self.cursor_position <= 0 end,

				move_cursor_next_word = function(self)
								while (not self.is_on_char) do
												if self:cursor_is_at_eol() then return end
												self:move_cursor(1)
								end
								while (self.is_on_char) do
												if self:cursor_is_at_eol() then return end
												self:move_cursor(1)
								end
				end,

				move_cursor_last_word = function(self)
								while (not self.is_on_char) do
												if self:cursor_is_at_bol() then return end
												self:move_cursor(-1)
								end
								while (self.is_on_char) do
												if self:cursor_is_at_bol() then return end
												self:move_cursor(-1)
								end
				end,

				draw_cursor = function(self)
								if self.blink then return end
								local cp = self.cursor_position + 1
								if cp == string.len(self.command_string) + 1 or
												string.sub(self.command_string, cp, cp) == " " then
												self.is_on_char = false
								else
												self.is_on_char = true
								end
								_lg.setColor(1, 1, 1, 1)
								_lg.rectangle(self.is_on_char and 'line' or 'fill',
								              self.x + (cp - 1) * self.font_w + self.text_offset,
								              self.y, self.cursor_w, self.cursor_h)
				end,

				draw = function(self)
								if not self.active then return end
								self:draw_bar_outline()
								if self.output then self:draw_output() end
								self:draw_text()
								self:draw_cursor()
				end

}
